package gui;

import gameManager.GameManagerGUI;
import gameManager.GameManager;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;

import javax.swing.Box;
import javax.swing.JFrame;
import javax.swing.JButton;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JComboBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JRadioButton;
import javax.swing.JTextField;

import common.Piece;
import common.Side;

public class MainFrame extends JFrame implements GameManagerGUI {

    private GameManager gameManager;
    private String[] algorithmNames = {"Random Algorithm", "Alpha-beta"};
    private JTable gameTable = new JTable();
    private DominoGameTableModel dominoGameTableModel;
    private JButton newGameButton = new JButton("New game");
    private JTextField dealerSeed = new JTextField("1");
    private Piece piece;
    private JTextField upperPlayerDepth = new JTextField("1000");
    private JTextField upperPlayerHands = new JTextField("1000");
    private JTextField upperPlayerSeed = new JTextField("1");
    private JTable upperPlayerTable = new JTable();
    private JRadioButton humanUpperPlayer = new JRadioButton("Human player");
    private JRadioButton xptoUpperPlayer = new JRadioButton("XPTO");
    private JComboBox upperPlayerAlgorithms = new JComboBox(algorithmNames);
    private JTextField downPlayerDepth = new JTextField("1000");
    private JTextField downPlayerHands = new JTextField("1000");
    private JTextField downPlayerSeed = new JTextField("1");
    private JTable downPlayerTable = new JTable();
    private JRadioButton humanDownPlayer = new JRadioButton("Human player");
    private JRadioButton xptoDownPlayer = new JRadioButton("XPTO");
    private JComboBox downPlayerAlgorithms = new JComboBox(algorithmNames);

    public MainFrame() {
        try {
            jbInit();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void jbInit() throws Exception {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        this.setTitle("Domino");

        JPanel contentPane = (JPanel) this.getContentPane();
        contentPane.setLayout(new BorderLayout());

        JPanel configurationPanel = new JPanel();
        configurationPanel.setLayout(new BoxLayout(configurationPanel, BoxLayout.Y_AXIS));

        ButtonGroup upperPlayerGroup = new ButtonGroup();
        upperPlayerGroup.add(humanUpperPlayer);
        upperPlayerGroup.add(xptoUpperPlayer);
        JPanel radioButtonsPanel = new JPanel(new GridLayout(5, 1));
        radioButtonsPanel.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createTitledBorder("Upper player"),
                BorderFactory.createEmptyBorder(1, 1, 1, 1)));
        radioButtonsPanel.add(humanUpperPlayer);
        radioButtonsPanel.add(xptoUpperPlayer);
        humanUpperPlayer.setSelected(true);
        configurationPanel.add(radioButtonsPanel);

        JPanel algorithmPanel = new JPanel(new FlowLayout());
        algorithmPanel.add(new JLabel("XPTO algorithm: "));
        algorithmPanel.add(upperPlayerAlgorithms);
        upperPlayerAlgorithms.setSelectedIndex(0);
        radioButtonsPanel.add(algorithmPanel);

        JPanel depthPanel = new JPanel(new FlowLayout());
        depthPanel.add(new JLabel("Max. search depth: "));
        upperPlayerDepth.setColumns(4);
        depthPanel.add(upperPlayerDepth);
        radioButtonsPanel.add(depthPanel);

        JPanel handsPanel = new JPanel(new FlowLayout());
        handsPanel.add(new JLabel("Max. hands: "));
        upperPlayerHands.setColumns(4);
        handsPanel.add(upperPlayerHands);
        handsPanel.add(new JLabel("Upper agent seed: "));
        upperPlayerSeed.setColumns(2);
        handsPanel.add(upperPlayerSeed);
        radioButtonsPanel.add(handsPanel);

        ButtonGroup downPlayerGroup = new ButtonGroup();
        downPlayerGroup.add(humanDownPlayer);
        downPlayerGroup.add(xptoDownPlayer);
        JPanel radioButtonsPanel2 = new JPanel(new GridLayout(5, 1));
        radioButtonsPanel2.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createTitledBorder("Down player"),
                BorderFactory.createEmptyBorder(1, 1, 1, 1)));
        radioButtonsPanel2.add(humanDownPlayer);
        radioButtonsPanel2.add(xptoDownPlayer);
        humanDownPlayer.setSelected(true);
        configurationPanel.add(radioButtonsPanel2);
        downPlayerGroup.setSelected(xptoDownPlayer.getModel(), true);

        JPanel algorithmPanel2 = new JPanel(new FlowLayout());
        algorithmPanel2.add(new JLabel("XPTO algorithm: "));
        algorithmPanel2.add(downPlayerAlgorithms);
        downPlayerAlgorithms.setSelectedIndex(0);
        radioButtonsPanel2.add(algorithmPanel2);

        JPanel depthPanel2 = new JPanel(new FlowLayout());
        depthPanel2.add(new JLabel("Max. search depth: "));
        downPlayerDepth.setColumns(4);
        depthPanel2.add(downPlayerDepth);
        radioButtonsPanel2.add(depthPanel2);

        JPanel handsPanel2 = new JPanel(new FlowLayout());
        handsPanel2.add(new JLabel("Max. hands: "));
        downPlayerHands.setColumns(4);
        handsPanel2.add(downPlayerHands);
        handsPanel2.add(new JLabel("Down agent seed: "));
        downPlayerSeed.setColumns(2);
        handsPanel2.add(downPlayerSeed);
        radioButtonsPanel2.add(handsPanel2);

        JPanel dealerPanel = new JPanel();

        dealerPanel.add(new JLabel("Dealer seed:"));
        dealerSeed.setColumns(2);
        dealerPanel.add(dealerSeed);
        configurationPanel.add(dealerPanel);

        newGameButton.addActionListener(new NewGameButton_actionAdapter(this));
        dealerPanel.add(newGameButton);

        JPanel boardPanel = new JPanel();
        boardPanel.setLayout(new BoxLayout(boardPanel, BoxLayout.Y_AXIS));

        boardPanel.add(upperPlayerTable);
        boardPanel.add(Box.createRigidArea(new Dimension(0, 10)));
        boardPanel.add(gameTable);
        boardPanel.add(Box.createRigidArea(new Dimension(0, 10)));
        boardPanel.add(downPlayerTable);

        JPanel painelGlobal = new JPanel(new FlowLayout());
        painelGlobal.add(configurationPanel);
        painelGlobal.add(boardPanel);
        contentPane.add(painelGlobal);

        gameManager = new GameManager(this);

        gameTable.addMouseListener(new GameTable_mouseAdapter(this));
        configureTable(gameTable);
        configurePlayerTable(upperPlayerTable, true);
        configurePlayerTable(downPlayerTable, false);

        upperPlayerTable.addMouseListener(new GamePlayer_mouseAdapter(this, true));
        downPlayerTable.addMouseListener(new GamePlayer_mouseAdapter(this, false));

        pack();
    }

    private void configureTable(JTable table) {
        dominoGameTableModel = new DominoGameTableModel(gameManager.getCurrentState());
        gameTable.setModel(dominoGameTableModel);
        table.setDefaultRenderer(Object.class, new DominoGameCellRenderer());
        for (int i = 0; i < table.getColumnCount(); i++) {
            table.getColumnModel().getColumn(i).setPreferredWidth(Properties.CELL_WIDTH);
        }
        table.setRowHeight(Properties.CELL_HEIGHT);
        table.setBorder(BorderFactory.createLineBorder(Color.black));
        table.setIntercellSpacing(new Dimension(0, 0));
        table.setOpaque(false);

        table.setShowGrid(false);
    }

    private void configurePlayerTable(JTable table, boolean isCima) {
        table.setModel(new PlayerHandTableModel(gameManager.getCurrentState(), isCima));
        table.setDefaultRenderer(Object.class, new PlayerHandCellRenderer());
        for (int i = 0; i < table.getColumnCount(); i++) {
            table.getColumnModel().getColumn(i).setPreferredWidth(Properties.CELL_WIDTH);
        }
        table.setRowHeight(Properties.CELL_HEIGHT);
        table.setBorder(BorderFactory.createLineBorder(Color.black));
        table.setIntercellSpacing(new Dimension(0, 0));
        table.setOpaque(false);

        table.setShowGrid(true);
    }

    public void jNewGameButton_actionPerformed(ActionEvent e) {
        newGameButton.setEnabled(false);

        if (xptoUpperPlayer.isSelected()) {
            gameManager.createUpperPlayerXPTO(
                    Integer.parseInt(upperPlayerDepth.getText()), Integer.parseInt(upperPlayerHands.getText()), Integer.parseInt(upperPlayerSeed.getText()),
                    (upperPlayerAlgorithms.getSelectedIndex() == 0) ? GameManager.RANDOM_ALGORITHM : GameManager.ALPHA_BETA);
        }

        if (xptoDownPlayer.isSelected()) {
            gameManager.createDownPlayerXPTO(
                    Integer.parseInt(downPlayerDepth.getText()), Integer.parseInt(downPlayerHands.getText()), Integer.parseInt(downPlayerSeed.getText()),
                    (downPlayerAlgorithms.getSelectedIndex() == 0) ? GameManager.RANDOM_ALGORITHM : GameManager.ALPHA_BETA);
        }

        gameManager.startGame(Integer.parseInt(dealerSeed.getText()));
    }

    void gameTable_mouseClicked(MouseEvent e) {
        if (piece != null) {
            int row = gameTable.rowAtPoint(e.getPoint());
            int col = gameTable.columnAtPoint(e.getPoint());
            if (dominoGameTableModel.isLeft(row, col)) {
                gameManager.humanMove(piece, Side.LEFT);
            } else if (dominoGameTableModel.isRight(row, col)) {
                gameManager.humanMove(piece, Side.RIGHT);
            }
            piece = null;
        }
    }

    void playerTable_mouseClicked(MouseEvent e, boolean isUpperPlayer) {
        if (isUpperPlayer && xptoUpperPlayer.isSelected() || !isUpperPlayer && xptoDownPlayer.isSelected()) {
            piece = null;
            return;
        }
        int column = isUpperPlayer ? upperPlayerTable.columnAtPoint(e.getPoint()) : downPlayerTable.columnAtPoint(e.getPoint());
        piece = isUpperPlayer ? gameManager.getUpperPlayerPieceAt(column) : gameManager.getDownPlayerPieceAt(column);

    }

    @Override
    public void showWinner(String winner) {
        if (winner.equals("Draw")) {
            JOptionPane.showMessageDialog(this, "Draw!", "Game End", JOptionPane.INFORMATION_MESSAGE);
        } else if (winner.equals("Upper player") && xptoUpperPlayer.isSelected()
                || winner.equals("Down player") && xptoDownPlayer.isSelected()
                || !xptoUpperPlayer.isSelected() || !xptoDownPlayer.isSelected()) {
            JOptionPane.showMessageDialog(this, winner + " wins!", "Game End", JOptionPane.INFORMATION_MESSAGE);
        } else {
            JOptionPane.showMessageDialog(this, "Congratulations, you win!", "Game End", JOptionPane.INFORMATION_MESSAGE);
        }
        newGameButton.setEnabled(true);
    }
}

class NewGameButton_actionAdapter implements ActionListener {

    private MainFrame adaptee;

    NewGameButton_actionAdapter(MainFrame adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        adaptee.jNewGameButton_actionPerformed(e);
    }
}

class GameTable_mouseAdapter extends java.awt.event.MouseAdapter {

    private MainFrame adaptee;

    GameTable_mouseAdapter(MainFrame adaptee) {
        this.adaptee = adaptee;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        adaptee.gameTable_mouseClicked(e);
    }
}

class GamePlayer_mouseAdapter extends java.awt.event.MouseAdapter {

    private MainFrame adaptee;
    private boolean isUpperPlayer;

    GamePlayer_mouseAdapter(MainFrame adaptee, boolean isUpperPlayer) {
        this.adaptee = adaptee;
        this.isUpperPlayer = isUpperPlayer;
    }

    @Override
    public void mouseClicked(MouseEvent e) {
        adaptee.playerTable_mouseClicked(e, isUpperPlayer);
    }
}
